package com.jp.fm.servlet;

import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;

import com.jp.fm.data.Account;
import com.jp.fm.data.Category;
import com.jp.fm.data.Transaction;

public class ShowTransactions extends ServletParent {

	private static final int YEAR_2012 = 2012;
	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Constructor of the object.
	 */
	public ShowTransactions() {
		super();
	}

	/**
	 * Destruction of the servlet. <br>
	 */
	public final void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	public final void doGet(final HttpServletRequest request,
			final HttpServletResponse response) throws ServletException,
			IOException {

		doPost(request, response);
	}

	public final void doPost(final HttpServletRequest request,
			final HttpServletResponse response) throws ServletException,
			IOException {

		// Get data for the left nav bar
		getPageService().composeRequestLeftNav(request);

		List<Transaction> trans = null;
		String[] categoryIdArray = null;
		String categoryThisMonth = request.getParameter("categoryThisMonth");
		String categoryIdStr = request.getParameter("searchCategory");
		String categoryUnConfirmedStr = request
				.getParameter("searchCategoryUnconfirmed");
		String categoryNameStr = request.getParameter("searchCategoryName");
		String transIdStr = request.getParameter("searchTransactionId");
		String accountStr = request.getParameter("acct");
		String keyword = request.getParameter("keyword");

		categoryIdArray = createCategoryIdArray(categoryIdStr, categoryNameStr);

		Date startDate = populateDate(request, "startDateDatepicker");
		Date endDate = populateDate(request, "endDateDatepicker");

		// Find transactions for a category since the beginning of this month
		if (null != categoryThisMonth) {
			trans = composeTransactionsCurrentMonth(request, categoryThisMonth);
			// Find transactions based on search criteria
		} else if (null != categoryIdArray || null != startDate
				|| null != endDate || null != keyword) {
			trans = composeTransactionsSearch(request, categoryIdArray,
					keyword, startDate, endDate);
		} else if (null != categoryUnConfirmedStr) {
			trans = composeTransactionsUnconfirmed(request);
		} else if (null != transIdStr) {
			trans = composeTransactionsById(request, transIdStr);
			// Find transactions for the given account since the beginning of
			// the month
		} else if (null != accountStr) {
			trans = composeTransactionsByAccount(request);
		}

		Set<Category> allCategories = getCategoryDao().findAll();
		allCategories.add(new Category(null, " "));
		request.setAttribute("allCategories", allCategories);

		if (null != trans) {
			Collections.sort(trans, new Transaction.DescendingComparator());
		}
		request.setAttribute("trans", trans);

		RequestDispatcher rd = request
				.getRequestDispatcher("/secured/Transactions.jsp");
		rd.forward(request, response);
	}

	protected Date populateDate(final HttpServletRequest request,
			final String key) {
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
		Date date = null;
		try {
			String dateStr = request.getParameter(key);
			if (StringUtils.isNotEmpty(dateStr)) {
				date = sdf.parse(dateStr);
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}

	protected List<Transaction> composeTransactionsByAccount(
			final HttpServletRequest request) {
		Calendar now = new GregorianCalendar();
		Calendar startMonth = new GregorianCalendar();
		startMonth.set(Calendar.DAY_OF_MONTH, 1);
		String acctStr = request.getParameter("acct");
		Account account = getAccountDao().findByName(acctStr);
		request.setAttribute("account", account);

		// Get rewards and fees if any
		Float rewardsAmount = getTransactionService()
				.calculateTransactionsTotal(
						getAccountService().getAccountRewards(account));
		if (null != rewardsAmount && rewardsAmount > 0) {
			request.setAttribute("rewards", rewardsAmount);
		}

		Float feesAmount = getTransactionService().calculateTransactionsTotal(
				getAccountService().getAccountFees(account));
		if (null != feesAmount && feesAmount > 0) {
			request.setAttribute("fees", feesAmount);
		}

		List<Transaction> trans = getTransactionDao().findByAny(null,
				startMonth.getTime(), now.getTime(), acctStr, null, null);
		return trans;
	}

	protected List<Transaction> composeTransactionsById(
			final HttpServletRequest request, final String transIdStr) {

		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = getTransactionDao().findById(
				Integer.valueOf(transIdStr));
		trans.add(tran);

		request.setAttribute("categoryAmount", tran.getAmount());

		return trans;
	}

	protected List<Transaction> composeTransactionsUnconfirmed(
			final HttpServletRequest request) {
		List<Transaction> trans = getTransactionDao().findByAny(null, null,
				null, null, null, Boolean.FALSE);

		Float categoryAmount = getTransactionService()
				.calculateTransactionsTotal(trans);
		request.setAttribute("categoryAmount", categoryAmount);
		return trans;
	}

	protected List<Transaction> composeTransactionsSearch(
			final HttpServletRequest request, final String[] categoryIdArray,
			final String keyword, final Date startDate, final Date endDate) {
		List<Category> categoryList = new ArrayList<Category>();
		List<Transaction> trans = new ArrayList<Transaction>();
		if (null != categoryIdArray) {
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < categoryIdArray.length; i++) {
				categoryList.add(getCategoryDao().findById(
						Integer.parseInt(categoryIdArray[i])));
				sb.append(categoryList.get(i).getName()).append(", ");
			}
			sb.deleteCharAt(sb.length() - 2);
			request.setAttribute("category", sb.toString().trim());
		}

		if (null != startDate) {
			request.setAttribute("startDate", startDate);
			if (null == endDate) {
				// Set end date to now
				request.setAttribute("endDate", new Date());
			}
		}

		if (null != endDate) {
			request.setAttribute("endDate", endDate);
			if (null == startDate) {
				// Set start date to past
				request.setAttribute("startDate", new GregorianCalendar(
						YEAR_2012, 0, 0).getTime());
			}
		}

		// If we have any categories then use them along with the other
		// vars to find the transactions
		if (categoryList.size() > 0) {
			for (Category category : categoryList) {
				trans.addAll(getTransactionDao().findByAny(category, startDate,
						endDate, null, keyword, null));
			}
		} else {
			// Just use the other vars to search for transactions
			trans.addAll(getTransactionDao().findByAny(null, startDate,
					endDate, null, StringUtils.trimToNull(keyword), null));
		}

		Float categoryAmount = getTransactionService()
				.calculateTransactionsTotal(trans, categoryList);
		request.setAttribute("categoryAmount", categoryAmount);
		request.setAttribute("keyword", keyword);

		return trans;
	}

	protected List<Transaction> composeTransactionsCurrentMonth(
			final HttpServletRequest request, final String categoryThisMonth) {

		Calendar now = new GregorianCalendar();
		Calendar startMonth = new GregorianCalendar();
		startMonth.set(Calendar.DAY_OF_MONTH, 1);

		request.setAttribute("category", categoryThisMonth);

		Category cat = getCategoryDao().findByName(categoryThisMonth);

		List<Transaction> trans = getTransactionDao().findByAny(cat,
				startMonth.getTime(), now.getTime(), null, null, null);
		Float categoryAmount = getTransactionService()
				.calculateTransactionsTotal(trans, cat);
		request.setAttribute("categoryAmount", categoryAmount);
		return trans;
	}

	/**
	 * Contains logic to retrieve an array of category IDs as Strings
	 * @param categoryNames
	 * @return array of category IDs as Strings
	 */
	protected String[] createCategoryIdArray(final String categoryIdStr,
			final String categoryNames) {

		String[] categoryIdArray = null;

		if (StringUtils.isNotEmpty(categoryIdStr)) {
			categoryIdArray = new String[1];
			categoryIdArray[0] = categoryIdStr;
		} else if (StringUtils.isNotEmpty(categoryNames)) {

			// If we cannot find the category try splitting it out by comma
			// because
			// it might be from the trends pie chart "Others" category
			if (StringUtils.startsWith(categoryNames, "Others")) {
				StringTokenizer st1 = new StringTokenizer(categoryNames, ":");
				st1.nextToken();
				String categoriesStr = st1.nextToken();
				// Now tokenize on comma
				StringTokenizer st2 = new StringTokenizer(categoriesStr, ",");
				List<String> catIdList = new ArrayList<String>();
				while (st2.hasMoreTokens()) {
					catIdList.add(getCategoryDao()
							.findByName(st2.nextToken().trim()).getId()
							.toString());
				}
				categoryIdArray = catIdList.toArray(new String[0]);

			} else {
				String categoryNamesStr = getCategoryDao()
						.findByName(categoryNames).getId().toString();
				categoryIdArray = new String[1];
				categoryIdArray[0] = categoryNamesStr;
			}
		}

		return categoryIdArray;
	}
}
